﻿<template>
    <my-dialog ref="dlg" v-model="state.dlgVisible" :title="cfg.title" :width="cfg.width" @closed="dlgClosed">

        <el-skeleton v-if="_loading" :rows="6" animated />

        <el-form @submit.prevent v-show="!_loading" ref="elForm" :model="formData" :rules="rules2" :label-width="cfg.label_width"
            :label-position="cfg.label_position" :disabled="state.readonly">
            <template v-for="item in formItems" :key="item">
                <my-form-row :tb="tb" :row="item" :formData="formData" :computeData="computeData"></my-form-row>
            </template>
        </el-form>

        <template #footer v-if="!_loading">

            <el-space wrap>
                <el-button @click="doCancel()" type="default">{{ getLabel("取消") }}</el-button>

                <!--自定义功能按钮-->
                <template v-for="btn in cfg.actions" :key="btn.label">
                    <el-dropdown v-if="btn.options && btn.label && checkIf(btn.vif)" split-button :type="btn.type"
                        @command="doClick(btn.click, $event)" @click="doClick(btn.click, $event)">
                        {{ btn.label }}
                        <template #dropdown>
                            <el-dropdown-menu>
                                <template v-for="menu in btn.options" :key="menu">
                                    <el-dropdown-item v-if="menu.label != '-'" :command="menu.value">{{ menu.label
                                    }}</el-dropdown-item>
                                    <el-dropdown-item v-else divided></el-dropdown-item>
                                </template>
                            </el-dropdown-menu>
                        </template>
                    </el-dropdown>

                    <el-button :type="btn.type" v-else-if="btn.label && checkIf(btn.vif)" @click="doClick(btn.click)">{{
                        getLabel(btn.label) }}</el-button>
                </template>

                <el-button v-if="!state.readonly && allowDelete2" type="danger" @click="doDelete()">{{ getLabel("删除")
                }}</el-button>

                <!--如果没有自定义按钮则显示默认保存按钮-->
                <el-button v-if="!state.readonly && allowSave" type="primary" @click="doSave()">{{ getLabel("保存")
                }}</el-button>

            </el-space>
        </template>

        <!--为自定义功能做准备-->
        <div style="display: none">
            <component :is="state.cform_com" ref="cform" v-bind="state.cform_cfg"></component>
        </div>
    </my-dialog>
</template>

<script>
import { computed, ErrorCodes, toRaw } from "vue";
import { Utils } from "./main";
import mydate from './mydate.js';
import store from "./store";


export default {
    name: "MyFormDlg",
    props: {
        tbname: {
            type: String,
            required: true,
        },
        formName: {
            type: String,
            default: "form",
        },
        saveThenClose: {
            type: Boolean,
            default: true
        },
        allowDelete: {
            type: Boolean,
            default: true
        },
        defaultCreate: Object,
        saveThenReload: Boolean,
        pForm: Object,
        extProp: Object
    },
    provide() {
        return {
            MyPage: this,
        };
    },
    emits: ["added", "updated", "deleted", "close"],
    data() {
        return {
            tb: {},
            state: {
                dlgVisible: false,
                readonly: false,
                cform_com: String,
                cform_cfg: Object,
            },
            cfg: {
                title: "",
                width: "800px",
                label_position: "right",
                label_width: "80px",
                actions: [],
                items: [],
            },
            formData: {
                ID: 0,
            },
            oldFormData: {},
            rules: {},
            //动态增加的计算字段
            computeData: {},
            _loading: false
        };
    },
    created() {
        this._tb = null;//getDM的过程(可能是异步加载)，open和view，需要等待这个过程完成以后才能执行
        this.initByTbName(this.tbname);
    },
    unmounted() {
        if (this.tbname) {
            Utils.attachStyle(this.tbname, "");
        }
        if (this.tb.unmounted && typeof (this.tb.unmounted) == "function") {
            this.tb.unmounted.apply(this);
        }
    },
    methods: {
        initByTbName(_tbname) {
            if (!_tbname) return;
            this._tb = this.$server.getDM(_tbname).then((tb) => {
                //把自定义组件注册到页面
                if (tb._Components) {
                    if (!this.$options.components) {
                        this.$options.components = {};
                    }
                    Object.keys(tb._Components).forEach(key => {
                        let cmp = toRaw(tb._Components[key]);
                        this.$options.components[key] = cmp;
                    });
                }

                this.rules = tb.rules || {};
                for (let key in this.rules) {
                    let rule1 = this.rules[key];
                    //为验证规则附加form和formData, 自定义验证的时候，如果涉及多个字段会很有用
                    rule1.myform = this;
                }

                this.tb = { ...tb };
                var cfg = tb[this.formName || "form"];
                if (cfg.columns && Array.isArray(cfg.columns)) {
                    //如果有自定义column，替换已有的
                    if(this.tb.columns!=cfg.columns){
                        let cls = this.tb.columns.map(x=>x);
                        cfg.columns.forEach(c=>{
                            let ix = cls.findIndex(x1=>x1.code==c.code);
                            if(ix!=-1){
                                cls.splice(ix, 1, c)
                            }else{
                                cls.push(c);
                            }
                        })
                        this.tb.columns = cls;
                    }
                    this.rules = Utils.buildRules(this.tb.columns);
                }
                if (cfg.readonly == true) {
                    this.state.readonly = true;
                }

                if (this.$mbh5) {
                    if (cfg.label_position2) {
                        cfg.label_position = cfg.label_position2;
                    } else {
                        cfg.label_position = "top";
                        cfg.width = "100%";
                    }
                }

                if (typeof cfg.actions != "object") {
                    cfg.actions = [];
                }

                cfg.actions.forEach(async btn => {
                    if (btn.options) {
                        btn.options = await Utils.parseOptions(btn.options);
                    }
                })

                cfg.title = cfg.title || tb.title;
                this.cfg = cfg;
                if (this.cfg.style) {
                    Utils.attachStyle(this.tbname, this.cfg.style);
                }

                //处理form扩展
                this.proceformExtend();

                if (this.cfg.vue && this.cfg.vue.created) {
                    this.doClick(this.cfg.vue.created);
                }
                return this.cfg;
            });
        },
        getField(code, items) {
            items = items || this.cfg.items;
            for (let i = 0; i < items.length; i++) {
                let item = items[i];
                if (item.code && item.code == code) return item;
                if (item.items) {
                    let field = this.getField(code, item.items);
                    if (field) return field;
                }
                if (item.fields) {
                    let field = this.getField(code, item.fields);
                    if (field) return field;
                }
            }
        },
        setFieldLabel(code, label) {
            let field = this.getField(code);
            if (field) {
                field.label = label;
            }
        },
        view(id, item) {
            return Promise.resolve(this._tb).then((res) => {
                this.state.readonly = true;
                this._loading = true;
                return this.open(id, item).then(() => {
                    this.state.readonly = true;
                    setTimeout(() => {
                        this._loading = false;
                    }, 100);
                });
            })
        },
        setDefaultValue() {
            //设置默认值
            if (this.tb) {
                //上级list页面的搜索条件，form页面中取默认值可能会用到
                let psearch = (this.$parent && this.$parent.search) ? this.$parent.search : {};

                for (var ix = 0; ix < this.tb.columns.length; ix++) {
                    var col = this.tb.columns[ix];
                    if (col.default != undefined) {
                        this.formData[col.code] = mydate.getDefaultValue(col.default, psearch);
                    } else {
                        if (col.type == "int" || col.type == "number") {
                            if (col.control == "select" || col.control == "selectinput" || col.control == "combo") {
                                if (col.options && col.options.length > 0) {
                                    this.formData[col.code] = col.options[0].value;
                                } else {
                                    this.formData[col.code] = null;
                                }
                            } else if (col.control == "number" || col.control == "el-number" || col.control == "ElNumber"){
                                this.formData[col.code] = 0;
                            }

                        } else if (col.type == "string") {
                            this.formData[col.code] = "";
                        } else if (col.type == "array") {
                            this.formData[col.code] = [];
                        }
                    }
                }
            }
            //ID需要初始化，这个字段必须
            this.formData["ID"]=0;
            this.oldFormData = { ...this.formData };
        },

        open(id, item) {
            return Promise.resolve(this._tb).then((res) => {
                this._open(id, item);
            });
        },

        _open(id, item) {
            id = id || 0;
            this.formData = { ID: 0 };
            this.oldFormData = { ID: 0 };
            if (this.$refs.elForm) {
                try {
                    this.$refs.elForm.resetFields();
                    this.$refs.elForm.clearValidate();
                } catch (e) {
                    console.log(e);
                }
            }
            this.setDefaultValue();

            if (!id && this.tbname) {
                this.load4Cache();
            }

            if (typeof this.onBeforeOpen == "function") {
                this.onBeforeOpen();
            }

            if (typeof (id) == "object") {
                item = id;
            }
            this._id_ = id;
            if (id && !item) {
                this.loadData();
            } else {
                if (typeof item == "object") {
                    //this.formData里面有初始默认值
                    this.formData = { ...this.formData, ...item };
                    id = item.ID || 0;
                } else if (typeof id == "object") {
                    this.formData = id;
                    id = this.formData.ID || this.formData.id || 0;
                }
                if (id > 0 && typeof this.onAfterOpen == "function") {
                    this.onAfterOpen(id, this.formData);
                }
            }
            if (!id) {
                //默认初始值
                if (this.defaultCreate) {
                    this.formData = {...this.defaultCreate, ...this.formData};
                }
                if (typeof this.onCreated == "function") {
                    this.onCreated();
                }
                if (typeof this.onAfterOpen == "function") {
                    this.onAfterOpen(0, this.formData);
                }
            }

            this.oldFormData = { ...this.formData };
            this.state.dlgVisible = true;
        },

        loadData() {
            let test = (typeof this._id_ == "number" && this._id_ > 0) || (typeof this._id_ == "string" && this._id_ != "");
            if (test) {
                var el = this.$refs.elForm ? this.$refs.elForm.$el : null;
                this._loading = true;
                return this.$server.get(this.tbname, this._id_, el).then((res) => {
                    res._pformData = this.formData._pformData;
                    this.formData = res;
                    
                    if (typeof this.checkViewRight == "function") {
                        if(!this.checkViewRight(this.formData)){
                            $msg.alert("无权查看");
                            this.close();
                            return;
                        }
                    }

                    if (typeof this.onAfterLoadData == "function") {
                        this.onAfterLoadData(this.formData);
                    }
                    if (typeof this.onAfterOpen == "function") {
                        this.onAfterOpen(this._id_, this.formData);
                    }
                    if (this.$refs.elForm) {
                        try {
                            this.$refs.elForm.clearValidate();
                        } catch (e) {
                            console.log(e);
                        }
                    }
                    //下个周期再保存，formData可能经过了数据重整(例如格式化...)和res格式可能不一致了
                    this.$nextTick(() => {
                        this.oldFormData = { ...this.formData };
                    });
                }).catch(e => {
                    //无权访问的时候，直接关闭
                    if (e == "无权查看") {
                        this.state.readonly = true;
                    }
                }).finally(() => {
                    setTimeout(() => {
                        this._loading = false;
                    }, 100);
                })
            }
        },
        doCancel() {
            if (!this.formData.ID) {
                this.clearCache();
            }
            this.close();
        },
        close(updated) {
            this.state.dlgVisible = false;
            if (updated) {
                this.clearCache();
                //关闭收通知父组件，需要刷新数据
                this.$emit("updated", this.formData);
            }

            this.$emit("close", this.formData);
        },

        getCacheKey() {
            return this.tbname + "_" + store.user.sid + "_" + store.user.uid;
        },

        load4Cache() {
            let key = this.getCacheKey();
            var jsdata = localStorage.getItem(key);
            if (jsdata && jsdata.length > 10) {
                console.log("从暂存中取出：" + this.tbname);
                let fdata = JSON.parse(jsdata);
                if(!fdata.ID){
                    fdata.ID=0;
                }
                this.formData = { ...this.formData, ...fdata };
            }
        },

        clearCache() {
            let key = this.getCacheKey();
            localStorage.removeItem(key);
            console.log("清除暂存:" + this.tbname);
        },
        save2Cache() {
            if (!this.formData.ID) {
                let key = this.getCacheKey();
                let saveItem = { ...this.formData };
                delete saveItem["_pformData"];
                localStorage.setItem(key, JSON.stringify(saveItem));
                console.log("暂时保存:" + this.tbname);
            }
        },

        dlgClosed() {
            this.save2Cache();
            if(this.onClosed && typeof(onClosed)){
                this.onClosed.apply(this);
            }
            this.$emit("close");
        },
        doDelete() {
            if (typeof this._id_ == "object") {
                $msg.confirm(this.getLabel('确认删除吗'), 'warning').then(() => {
                    this.$emit("deleted", this._id_);
                    this.close();
                });
                return;
            } else {
                let msg = this.getLabel('确认删除吗');
                this.$server.delete(this.tbname, this.formData.ID, null, msg).then(() => {
                    this.clearCache();
                    $msg.message(this.getLabel("操作成功"));
                    this.$emit("deleted", this.formData);
                    this.close();
                });
            }
        },
        hasChange() {
            let s1 = JSON.stringify(this.formData);
            let s2 = JSON.stringify(this.oldFormData);
            return s1 != s2;
        },

        validate(showError) {
            if (!showError) {
                return this.$refs.elForm.validate();
            }
            return new Promise((resolve, reject) => {
                var me = this;
                this.$refs.elForm.validate(function (isOK, errs) {
                    if (!isOK && showError) {
                        var msgs = [];
                        Object.keys(errs).forEach(key => {
                            var items = errs[key];
                            if (items.forEach) {
                                items.forEach(item => {
                                    msgs.push(item.message);
                                })
                            }
                        })
                        $msg.error(msgs.join("<br />"));
                    } else {
                        resolve(isOK, errs);
                    }
                });
            });
        },
        doSave(closeAfterSave) {
            //是否保存完成以后，关闭
            var me = this;
            return this.$refs.elForm.validate(function (isOK, errs) {
                if (isOK) {
                    //如果有自定义的beforeSave， 保存前调用
                    //如有错误返回错误信息
                    if (me.beforeSave && typeof (me.beforeSave) == "function") {
                        let msg = me.beforeSave.apply(me);
                        if (msg) {
                            $msg.error(msg);
                            return;
                        }
                    }
                    me._innerSave(closeAfterSave);
                    return;
                }
                var msgs = [];
                Object.keys(errs).forEach(key => {
                    var items = errs[key];
                    if (items.forEach) {
                        items.forEach(item => {
                            msgs.push(item.message);
                        })
                    }
                })
                $msg.error(msgs.join("<br />"));
            });

            // if(!this.hasChange()){
            //     $msg.alert("数据没有变化，无需保存");
            //     return;
            // }

        },

        serverUpdate(entity) {
            let el = this.$refs?.elForm?.$el;
            if(entity["ID"]==entity["id"]){
                delete entity["ID"]
            }
            if (entity.ID || (entity["ID"] == 0 && entity["id"] == 0)) {
                delete entity["id"];
            }
            if (entity.id || (entity["ID"] == 0 && entity["id"] == 0)) {
                delete entity["ID"];
            }
            return this.$server.update(this.tbname, entity, el);
        },
        _innerSave(closeAfterSave) {
            closeAfterSave = (closeAfterSave == undefined ? this.saveThenClose : closeAfterSave);
            if (typeof this.onBeforeUpdate == "function") {
                var checkMessage = this.onBeforeUpdate(this.formData);
                if (checkMessage) {
                    $msg.alert(checkMessage);
                    return;
                }
            }
            if (typeof this._id_ == "object") {
                if (this.formData.ID == 0) {
                    this.$emit("added", this.formData);
                } else {
                    this.$emit("updated", this.formData);
                }
                this.oldFormData = { ...this.formData };
                if (closeAfterSave) {
                    this.close();
                }
                this.clearCache();
                return;
            }
            let pm = { ...this.formData };
            delete pm["_pformData"];//有时候上级窗口的值会绑定到_pformData， 不需要提交到服务器
            if (typeof this.onBeforeSave == "function") {
                let newpm = this.onBeforeSave(pm);
                if (newpm && typeof (newpm) == "object") {
                    pm = newpm;
                }
            }
            return this.serverUpdate(pm).then(res => {
                //解析新增ID
                if (!!res && typeof(res)=="string" && res.startsWith("OK:")) {
                    let newID = res.substring(3);
                    if (parseInt(newID) == newID) {
                        newID = parseInt(newID);
                        this.formData.ID = newID;
                        this._id_ = newID;
                    }
                }
                return res;
            }).then(res => {
                //清除缓存
                this.clearCache();

                //如果需要重新加载数据
                if (this.saveThenReload) {
                    let el = this.$refs.elForm ? this.$refs.elForm.$el : null;
                    return this.$server.get(this.tbname, this.formData.ID, el).then(newItem => {
                        this.formData = { ...this.formData, ...newItem };
                        return newItem;
                    });
                }
                return res;
            }).then(res => {

                var afterSavePromise = {};
                if (this.afterSave && typeof (this.afterSave) == "function") {
                    afterSavePromise = this.afterSave.apply(this);
                } 

                return Promise.resolve(afterSavePromise).then(_=>{
                    //触发事情通知
                    if (!this.oldFormData.ID && !this.oldFormData.id) {
                        this.$emit("added", this.formData);
                    } else {
                        this.$emit("updated", this.formData);
                    }

                    $msg.message("操作成功");
                    this.oldFormData = { ...this.formData };
                    if (closeAfterSave) {
                        this.close();
                    } else {
                        this.loadData();
                    }
                    return res;
                })

            });
        },
        checkIf(vif) {
            if (typeof vif == "function") {
                return vif.apply(this);
            } else if (typeof vif == "string") {
                if(vif in this.computeData){
                    return this.computeData[vif] != false;
                }
                if(vif in this){
                    return Utils.doFunc(vif, this, [this.formData]);
                }
            }
            return true;
        },
        doClick(func, event) {
            Utils.doFunc(func, this, event!=undefined ? [event] : undefined);
        },
        proceformExtend() {
            if (this.tb.ext) {
                //通用扩展+form专用扩展
                if (this.tb.ext.common) {
                    let ext = this.tb.ext.common;
                    //动态添加watch
                    this.proceFormWatch(ext.watch);
                    //动态添加计算属性
                    this.proceFormComputed(ext.computed);
                    //添加动态method
                    this.proceFormMethods(ext.methods);
                }

                if (this.tb.ext.form) {
                    let ext = this.tb.ext.form;
                    //动态添加watch
                    this.proceFormWatch(ext.watch);
                    //动态添加计算属性
                    this.proceFormComputed(ext.computed);
                    //添加动态method
                    this.proceFormMethods(ext.methods);
                }
            }
            if (this.cfg.vue) {
                //动态添加watch
                this.proceFormWatch(this.cfg.vue.watch);
                //动态添加计算属性
                this.proceFormComputed(this.cfg.vue.computed);
                //添加动态method
                this.proceFormMethods(this.cfg.vue.methods);
            }
        },
        proceFormWatch(w) {
            for (let key in w) {
                let val = w[key];
                if (typeof val == "function") {
                    this.$watch(key, (newVal, oldVal) => {
                        //加载数据时、只读时候不触发watch
                        if (this._loading || (this.state && this.state.readonly)) return;
                        try {
                            val.apply(this, [newVal, oldVal]);
                        } catch (e) {
                            alert("自定义watch执行错误:" + e);
                        }
                    },
                        { deep: true });
                }
            }
        },
        proceFormComputed(c) {
            for (let key in c) {
                let cfield = c[key];
                if (typeof cfield == "function") {
                    this.computeData[key] = computed(() => cfield.apply(this));
                } else if (typeof cfield.get == "function") {
                    this.computeData[key] = computed({
                        get: () => {
                            try {
                                return cfield.get.apply(this);
                            } catch (e) {
                                console.log("计算字段取值出错：" + key);
                            }
                        },
                        set: (newValue) => {
                            try {
                                cfield.set.apply(this, [newValue]);
                            } catch (e) {
                                console.log(
                                    "计算字段Set出错：" + key + "," + newValue
                                );
                            }
                        },
                    });
                }
                if (key.startsWith("_")) {
                    this.formData[key] = this.computeData[key];
                }
            }
        },
        proceFormMethods(m) {
            for (let key in m) {
                let func = m[key];
                if (typeof func == "function") {
                    this[key] = func.bind(this);
                } else {
                    console.log("method定义错误：" + key);
                }
            }
        },
        getLabel(label) {
            let labels = this.cfg.labels || this.tb.labels;
            if (labels) {
                return labels[label] || label;
            }
            return label;
        },
    },
    computed: {
        allowDelete2() {
            if(this.formData.ID==0)return false;
            
            if ("allowDelete" in this.computeData) {
                return this.computeData["allowDelete"];
            }
            if (this.cfg.allow && this.cfg.allow.delete == false) {
                return false;
            }

            //手机版页面才有删除按钮
            return (
                this.$mbh5 &&
                this.allowDelete &&
                !this.state.readonly &&
                this.cfg.actions.length == 0 &&
                (this.formData.ID > 0 || this.formData.ID != "")
            );
        },
        allowSave() {
            if ("allowSave" in this.computeData) {
                return this.computeData["allowSave"];
            }
            return !this.state.readonly && this.cfg.actions.length == 0;
        },
        formItems() {
            return this.cfg.items;
        },

        rules2() {
            if (this.state.readonly == true) return {};
            if (!this.tb.columns)return{};
            //校验规则也可以指定vif条件，如果指定了vif条件， 只有满足条件的时候该条规则才生效
            let rules2 = {};
            for (let key in this.rules) {
                let field = this.tb.columns.find(p => p.code == key);
                if (field) {
                    let p = this.rules[key];
                    let a1 = p.filter(p1 => {
                        return !p1.vif || this.checkIf(p1.vif);
                    });
                    if(p.required==true && !field.required){
                        //如果原来有必填要求，现在没有了就去掉必填要求
                        continue;
                    }
                    if (a1.length > 0) {
                        rules2[key] = a1;
                    }
                }
            }
            //初始没有必填要求，后面出现的
            this.tb.columns.filter(x=>x.required==true).forEach(col=>{
                let p1 = rules2[col.code];
                if(!p1 && (!col.vif || this.checkIf(col.vif))){
                    let tp = undefined;
                    switch (col.type.toLowerCase()) {
                        case "string":
                        case "text":
                            tp = "string";
                            break;
                        case "bool":
                        case "boolean":
                            tp = "boolean";
                            break;
                        case "int":
                            tp = "integer"
                            break;
                        case "array":
                            tp = "array";
                            break;
                        case "date":
                        case "datetime":
                        case "time":
                            tp = "date";
                            break;
                        case "object":
                            tp = "object";
                            break;
                        case "number":
                        case "number2":
                        case "number4":
                            tp = "number";
                            break;
                    };
                    let rule = { type: tp, required: true, trigger: 'blur', message: `请录入${col.label}` };
                    if (col.type == "string") {
                        rule.validator = (rule, value) => {
                            return value != undefined && value != null && (String(value).length > 0);
                        }
                    }
                    rules2[col.code] = rule;
                }
            })

            return rules2;
        }

    },
    watch: {
        tbname(val) {
            this.initByTbName(val);
        },
        rules2() {
            //规则变化，需要重新验证
            if (this.$refs.elForm) {
                this.$refs.elForm.clearValidate();
            }
        }
    }
};
</script>

<style scoped>
.el-button {
    padding: 4px 8px;
}

.el-button+.el-button {
    margin-left: 6px;
}</style>